---
title: 函數和作用域機制
description: 深入理解 Python 和 JavaScript 的函數定義、呼叫機制、作用域規則和閉包概念，掌握高階函數和函數式程式設計模式。
---

# 函數和作用域機制

函數是程式設計的核心構建塊，Python 和 JavaScript 都支援函數式程式設計，但在函數定義、作用域規則和閉包行為上有重要差異。本模組將幫助你理解這些差異，掌握在 JavaScript 中有效使用函數的技巧。

## 函數定義和呼叫

### 基本函數定義

<PythonEditor title="函數定義方式" compare={true}>
```python !! py
# Python 函數定義

# 1. 基本函數定義
def greet(name):
    """問候函數"""
    return f"Hello, {name}!"

# 2. 帶預設參數的函數
def greet_with_title(name, title="Mr./Ms."):
    """帶預設參數的問候函數"""
    return f"Hello, {title} {name}!"

# 3. 可變參數函數
def sum_numbers(*args):
    """計算可變數量數字的和"""
    return sum(args)

def print_info(**kwargs):
    """列印關鍵字參數資訊"""
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# 4. 混合參數函數
def complex_function(required, default="default", *args, **kwargs):
    """複雜參數函數"""
    print(f"Required: {required}")
    print(f"Default: {default}")
    print(f"Args: {args}")
    print(f"Kwargs: {kwargs}")

# 函數呼叫範例
print(greet("Alice"))
print(greet_with_title("Bob", "Dr."))
print(sum_numbers(1, 2, 3, 4, 5))

print_info(name="Charlie", age=30, city="New York")

complex_function("test", "custom", 1, 2, 3, extra="value")

# 5. Lambda 函數
square = lambda x: x ** 2
add = lambda x, y: x + y

print(f"Square of 5: {square(5)}")
print(f"Add 3 and 4: {add(3, 4)}")

# 6. 函數作為第一類物件
def multiply(x, y):
    return x * y

# 函數賦值
operation = multiply
result = operation(4, 5)
print(f"Operation result: {result}")

# 函數作為參數
def apply_operation(a, b, func):
    return func(a, b)

result = apply_operation(6, 7, multiply)
print(f"Apply operation: {result}")
```

```javascript !! js
// JavaScript 函數定義

// 1. 函數宣告
function greet(name) {
    // 函數體
    return `Hello, ${name}!`;
}

// 2. 函數表達式
const greetExpression = function(name) {
    return `Hello, ${name}!`;
};

// 3. 箭頭函數（ES6+）
const greetArrow = (name) => {
    return `Hello, ${name}!`;
};

// 簡化箭頭函數
const greetSimple = name => `Hello, ${name}!`;

// 4. 帶預設參數的函數（ES6+）
function greetWithTitle(name, title = "Mr./Ms.") {
    return `Hello, ${title} ${name}!`;
}

// 5. 可變參數函數（Rest parameters）
function sumNumbers(...args) {
    return args.reduce((sum, num) => sum + num, 0);
}

function printInfo(requiredParam, ...otherArgs) {
    console.log(`Required: ${requiredParam}`);
    console.log(`Other args: ${otherArgs}`);
}

// 6. 物件解構參數
function printUserInfo({name, age, city = "Unknown"}) {
    console.log(`Name: ${name}, Age: ${age}, City: ${city}`);
}

// 函數呼叫範例
console.log(greet("Alice"));
console.log(greetWithTitle("Bob", "Dr."));
console.log(sumNumbers(1, 2, 3, 4, 5));

printInfo("required", "arg1", "arg2", "arg3");
printUserInfo({name: "Charlie", age: 30, city: "New York"});

// 7. 立即執行函數表達式（IIFE）
const result = (function(x, y) {
    return x + y;
})(5, 3);
console.log(`IIFE result: ${result}`);

// 8. 函數作為第一類物件
function multiply(x, y) {
    return x * y;
}

// 函數賦值
const operation = multiply;
const result2 = operation(4, 5);
console.log(`Operation result: ${result2}`);

// 函數作為參數
function applyOperation(a, b, func) {
    return func(a, b);
}

const result3 = applyOperation(6, 7, multiply);
console.log(`Apply operation: ${result3}`);

// 9. 方法定義（在物件中）
const calculator = {
    add(a, b) {
        return a + b;
    },
    
    subtract: function(a, b) {
        return a - b;
    },
    
    multiply: (a, b) => a * b  // 注意：箭頭函數沒有自己的 this
};

console.log(`Calculator add: ${calculator.add(10, 5)}`);
console.log(`Calculator subtract: ${calculator.subtract(10, 5)}`);
console.log(`Calculator multiply: ${calculator.multiply(10, 5)}`);
```
</PythonEditor>

### 參數處理

<PythonEditor title="參數處理機制" compare={true}>
```python !! py
# Python 參數處理

# 1. 位置參數和關鍵字參數
def process_data(data, format_type="json", verbose=False, **options):
    """處理資料的函數"""
    print(f"Data: {data}")
    print(f"Format: {format_type}")
    print(f"Verbose: {verbose}")
    if options:
        print(f"Options: {options}")

# 不同的呼叫方式
process_data("sample_data")
process_data("sample_data", "xml")
process_data("sample_data", verbose=True)
process_data("sample_data", format_type="csv", verbose=True, encoding="utf-8")

# 2. 強制關鍵字參數（Python 3+）
def create_user(name, *, age, email):
    """建立使用者，age 和 email 必須使用關鍵字參數"""
    return {
        "name": name,
        "age": age,
        "email": email
    }

# 必須使用關鍵字參數
user = create_user("Alice", age=25, email="alice@example.com")
print(f"Created user: {user}")

# 3. 參數解包
def add_three_numbers(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
result = add_three_numbers(*numbers)  # 解包清單
print(f"Sum with unpacking: {result}")

# 字典解包
def introduce(name, age, city):
    return f"I'm {name}, {age} years old, from {city}"

person_info = {"name": "Bob", "age": 30, "city": "London"}
introduction = introduce(**person_info)  # 解包字典
print(introduction)

# 4. 型別註解（Type hints）
from typing import List, Dict, Optional, Union, Callable

def calculate_average(numbers: List[float]) -> float:
    """計算平均值"""
    if not numbers:
        return 0.0
    return sum(numbers) / len(numbers)

def process_callback(data: List[int], callback: Callable[[int], int]) -> List[int]:
    """處理資料並應用回呼函數"""
    return [callback(item) for item in data]

# 使用型別註解的函數
average = calculate_average([1.5, 2.5, 3.5])
print(f"Average: {average}")

processed = process_callback([1, 2, 3, 4], lambda x: x * 2)
print(f"Processed: {processed}")

# 5. 函數重載（使用 functools.singledispatch）
from functools import singledispatch

@singledispatch
def process_value(value):
    """預設處理函數"""
    return f"Unknown type: {type(value)}"

@process_value.register
def _(value: int):
    return f"Integer: {value * 2}"

@process_value.register
def _(value: str):
    return f"String: {value.upper()}"

@process_value.register
def _(value: list):
    return f"List length: {len(value)}"

# 測試函數重載
print(process_value(42))
print(process_value("hello"))
print(process_value([1, 2, 3]))
print(process_value(3.14))
```

```javascript !! js
// JavaScript 參數處理

// 1. 預設參數和解構
function processData(data, {
    formatType = "json",
    verbose = false,
    encoding = "utf-8",
    ...options
} = {}) {
    console.log(`Data: ${data}`);
    console.log(`Format: ${formatType}`);
    console.log(`Verbose: ${verbose}`);
    console.log(`Encoding: ${encoding}`);
    if (Object.keys(options).length > 0) {
        console.log(`Options: ${JSON.stringify(options)}`);
    }
}

// 不同的呼叫方式
processData("sample_data");
processData("sample_data", {formatType: "xml"});
processData("sample_data", {verbose: true});
processData("sample_data", {
    formatType: "csv",
    verbose: true,
    compression: "gzip"
});

// 2. 參數驗證和型別檢查
function createUser(name, {age, email, ...options} = {}) {
    // 參數驗證
    if (typeof name !== 'string' || !name.trim()) {
        throw new Error("Name must be a non-empty string");
    }
    
    if (typeof age !== 'number' || age < 0) {
        throw new Error("Age must be a non-negative number");
    }
    
    if (typeof email !== 'string' || !email.includes('@')) {
        throw new Error("Email must be a valid string");
    }
    
    return {
        name,
        age,
        email,
        ...options
    };
}

// 使用物件參數模擬關鍵字參數
const user = createUser("Alice", {
    age: 25,
    email: "alice@example.com",
    city: "New York"
});
console.log(`Created user: ${JSON.stringify(user)}`);

// 3. 展開運算符和其餘參數
function addThreeNumbers(a, b, c) {
    return a + b + c;
}

const numbers = [1, 2, 3];
const result = addThreeNumbers(...numbers);  // 展開陣列
console.log(`Sum with spread: ${result}`);

// 物件展開
function introduce({name, age, city, ...otherInfo}) {
    const intro = `I'm ${name}, ${age} years old, from ${city}`;
    if (Object.keys(otherInfo).length > 0) {
        return `${intro}. Additional info: ${JSON.stringify(otherInfo)}`;
    }
    return intro;
}

const personInfo = {
    name: "Bob",
    age: 30,
    city: "London",
    profession: "Developer",
    hobby: "Reading"
};
const introduction = introduce(personInfo);
console.log(introduction);

// 4. 函數重載模擬
function processValue(value) {
    // 根據型別分發
    switch (typeof value) {
        case 'number':
            return `Number: ${value * 2}`;
        case 'string':
            return `String: ${value.toUpperCase()}`;
        case 'object':
            if (Array.isArray(value)) {
                return `Array length: ${value.length}`;
            } else if (value === null) {
                return "Null value";
            } else {
                return `Object keys: ${Object.keys(value).length}`;
            }
        case 'boolean':
            return `Boolean: ${!value}`;
        default:
            return `Unknown type: ${typeof value}`;
    }
}

// 測試函數重載
console.log(processValue(42));
console.log(processValue("hello"));
console.log(processValue([1, 2, 3]));
console.log(processValue({a: 1, b: 2}));
console.log(processValue(true));

// 5. 進階參數處理
class AdvancedFunctions {
    // 方法重載模擬
    static calculate(...args) {
        if (args.length === 1) {
            return args[0] ** 2;  // 平方
        } else if (args.length === 2) {
            return args[0] + args[1];  // 加法
        } else if (args.length === 3) {
            return args[0] * args[1] * args[2];  // 乘法
        } else {
            throw new Error("Unsupported number of arguments");
        }
    }
    
    // 柯里化函數
    static curry(fn) {
        return function curried(...args) {
            if (args.length >= fn.length) {
                return fn.apply(this, args);
            } else {
                return function(...args2) {
                    return curried.apply(this, args.concat(args2));
                };
            }
        };
    }
}

// 測試進階功能
console.log(AdvancedFunctions.calculate(5));        // 25
console.log(AdvancedFunctions.calculate(3, 4));     // 7
console.log(AdvancedFunctions.calculate(2, 3, 4));  // 24

// 柯里化範例
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = AdvancedFunctions.curry(multiply);
const multiplyBy2 = curriedMultiply(2);
const multiplyBy2And3 = multiplyBy2(3);
console.log(multiplyBy2And3(4));  // 24
```
</PythonEditor>

## 作用域和變數查找

### 作用域規則比較

<PythonEditor title="作用域機制" compare={true}>
```python !! py
# Python 作用域規則（LEGB）

# 全域變數
global_var = "I'm global"

def outer_function():
    # 封閉作用域（Enclosing scope）
    outer_var = "I'm in outer function"
    
    def inner_function():
        # 區域作用域（Local scope）
        local_var = "I'm local"
        
        # 存取不同作用域的變數
        print(f"Local: {local_var}")
        print(f"Outer: {outer_var}")
        print(f"Global: {global_var}")
        
        # 內建作用域（Built-in scope）
        print(f"Built-in len function: {len}")
    
    inner_function()
    return inner_function

# 測試作用域
outer_function()

# 1. global 關鍵字
counter = 0

def increment_global():
    global counter
    counter += 1
    print(f"Global counter: {counter}")

increment_global()
increment_global()

# 2. nonlocal 關鍵字
def make_counter():
    count = 0
    
    def increment():
        nonlocal count
        count += 1
        return count
    
    return increment

counter_func = make_counter()
print(f"Counter: {counter_func()}")  # 1
print(f"Counter: {counter_func()}")  # 2
print(f"Counter: {counter_func()}")  # 3

# 3. 作用域和變數遮蔽
name = "Global Alice"

def test_shadowing():
    name = "Local Bob"  # 遮蔽全域變數
    print(f"Inside function: {name}")

test_shadowing()
print(f"Outside function: {name}")

# 4. 清單推導式的作用域（Python 3+）
x = "global x"
squares = [x**2 for x in range(5)]  # x 在推導式中是區域的
print(f"After comprehension: {x}")  # 仍然是 "global x"

# 5. 函數定義時的作用域捕獲
functions = []
for i in range(3):
    # 使用預設參數捕獲當前值
    functions.append(lambda x=i: x)

for func in functions:
    print(func())  # 0, 1, 2

# 6. 變數生命週期
def create_closure():
    data = [1, 2, 3, 4, 5]
    
    def get_data():
        return data  # data 在閉包中保持活躍
    
    return get_data

getter = create_closure()
print(f"Closure data: {getter()}")

# 7. 模組層級的作用域
# __name__ 是模組層級的內建變數
if __name__ == "__main__":
    print("This is the main module")

# 匯入的作用域
import math
from datetime import datetime

print(f"Math pi: {math.pi}")
print(f"Current time: {datetime.now()}")
```

```javascript !! js
// JavaScript 作用域規則

// 全域作用域
var globalVar = "I'm global";
let globalLet = "I'm also global";
const globalConst = "I'm global too";

function outerFunction() {
    // 函數作用域
    var outerVar = "I'm in outer function";
    let outerLet = "I'm also in outer";
    
    function innerFunction() {
        // 內部函數作用域
        var localVar = "I'm local";
        let localLet = "I'm also local";
        
        // 存取不同作用域的變數
        console.log(`Local var: ${localVar}`);
        console.log(`Local let: ${localLet}`);
        console.log(`Outer var: ${outerVar}`);
        console.log(`Outer let: ${outerLet}`);
        console.log(`Global var: ${globalVar}`);
        console.log(`Global let: ${globalLet}`);
    }
    
    innerFunction();
    return innerFunction;
}

// 測試作用域
outerFunction();

// 1. var vs let vs const 的作用域差異
function scopeTest() {
    console.log("\n=== Scope Test ===");
    
    if (true) {
        var varVariable = "var in block";
        let letVariable = "let in block";
        const constVariable = "const in block";
    }
    
    console.log(`var outside block: ${varVariable}`);  // 可存取
    // console.log(letVariable);  // ReferenceError
    // console.log(constVariable);  // ReferenceError
}

scopeTest();

// 2. 變數提升（Hoisting）
function hoistingTest() {
    console.log("\n=== Hoisting Test ===");
    
    console.log(`var before declaration: ${typeof hoistedVar}`);  // undefined
    // console.log(hoistedLet);  // ReferenceError (temporal dead zone)
    
    var hoistedVar = "I'm hoisted";
    let hoistedLet = "I'm not hoisted";
    
    console.log(`var after declaration: ${hoistedVar}`);
    console.log(`let after declaration: ${hoistedLet}`);
}

hoistingTest();

// 3. 閉包和變數捕獲
let counter = 0;

function incrementGlobal() {
    counter++;
    console.log(`Global counter: ${counter}`);
}

incrementGlobal();
incrementGlobal();

// 建立閉包計數器
function makeCounter() {
    let count = 0;
    
    return function() {
        count++;
        return count;
    };
}

const counterFunc = makeCounter();
console.log(`Counter: ${counterFunc()}`);  // 1
console.log(`Counter: ${counterFunc()}`);  // 2
console.log(`Counter: ${counterFunc()}`);  // 3

// 4. 變數遮蔽
const name = "Global Alice";

function testShadowing() {
    const name = "Local Bob";  // 遮蔽外部變數
    console.log(`Inside function: ${name}`);
}

testShadowing();
console.log(`Outside function: ${name}`);

// 5. 迴圈中的閉包問題
console.log("\n=== Loop Closure Issues ===");

// 問題：使用 var
const functionsVar = [];
for (var i = 0; i < 3; i++) {
    functionsVar.push(function() {
        return i;  // 所有函數都引用同一個 i
    });
}

console.log("Using var:");
functionsVar.forEach(func => console.log(func()));  // 3, 3, 3

// 解決方案1：使用 let
const functionsLet = [];
for (let i = 0; i < 3; i++) {
    functionsLet.push(function() {
        return i;  // 每個 i 都是獨立的
    });
}

console.log("Using let:");
functionsLet.forEach(func => console.log(func()));  // 0, 1, 2

// 解決方案2：使用 IIFE
const functionsIIFE = [];
for (var i = 0; i < 3; i++) {
    functionsIIFE.push((function(index) {
        return function() {
            return index;
        };
    })(i));
}

console.log("Using IIFE:");
functionsIIFE.forEach(func => console.log(func()));  // 0, 1, 2

// 6. 模組作用域（ES6 模組）
// 在實際模組中，頂層宣告不會污染全域作用域
const moduleVariable = "I'm module-scoped";

// 7. this 關鍵字的作用域
const obj = {
    name: "Object",
    
    regularMethod: function() {
        console.log(`Regular method this: ${this.name}`);
        
        const innerFunction = function() {
            console.log(`Inner function this: ${this.name}`);  // undefined
        };
        innerFunction();
        
        const arrowFunction = () => {
            console.log(`Arrow function this: ${this.name}`);  // "Object"
        };
        arrowFunction();
    },
    
    arrowMethod: () => {
        console.log(`Arrow method this: ${this.name}`);  // undefined
    }
};

console.log("\n=== This Binding ===");
obj.regularMethod();
obj.arrowMethod();

// 8. 嚴格模式下的作用域
function strictModeTest() {
    'use strict';
    
    // 在嚴格模式下，this 不會自動綁定到全域物件
    function innerFunc() {
        console.log(`Strict mode this: ${this}`);  // undefined
    }
    
    innerFunc();
}

strictModeTest();
```
</PythonEditor>

## 閉包和高階函數

### 閉包機制

<PythonEditor title="閉包機制" compare={true}>
```python !! py
# Python 閉包機制

# 1. 基本閉包
def create_multiplier(factor):
    """建立一個乘法器閉包"""
    def multiplier(number):
        return number * factor  # factor 被閉包捕獲
    return multiplier

# 使用閉包
double = create_multiplier(2)
triple = create_multiplier(3)

print(f"Double 5: {double(5)}")  # 10
print(f"Triple 4: {triple(4)}")  # 12

# 2. 閉包修改外部變數
def create_accumulator(initial=0):
    """建立累加器閉包"""
    total = [initial]  # 使用清單避免 nonlocal
    
    def accumulate(value):
        total[0] += value
        return total[0]
    
    def get_total():
        return total[0]
    
    def reset():
        total[0] = initial
    
    # 回傳函數物件，附加方法
    accumulate.get_total = get_total
    accumulate.reset = reset
    
    return accumulate

acc = create_accumulator(10)
print(f"Accumulate 5: {acc(5)}")    # 15
print(f"Accumulate 3: {acc(3)}")    # 18
print(f"Current total: {acc.get_total()}")  # 18
acc.reset()
print(f"After reset: {acc.get_total()}")    # 10

# 3. 使用 nonlocal 的閉包
def create_counter(start=0, step=1):
    """建立計數器閉包"""
    count = start
    
    def increment():
        nonlocal count
        count += step
        return count
    
    def decrement():
        nonlocal count
        count -= step
        return count
    
    def get_value():
        return count
    
    # 回傳字典包含多個函數
    return {
        'increment': increment,
        'decrement': decrement,
        'get_value': get_value
    }

counter = create_counter(0, 2)
print(f"Initial: {counter['get_value']()}")      # 0
print(f"Increment: {counter['increment']()}")    # 2
print(f"Increment: {counter['increment']()}")    # 4
print(f"Decrement: {counter['decrement']()}")    # 2

# 4. 閉包捕獲迴圈變數（常見陷阱）
def create_functions_wrong():
    """錯誤的閉包建立方式"""
    functions = []
    for i in range(3):
        functions.append(lambda: i)  # 所有 lambda 共享同一個 i
    return functions

def create_functions_correct():
    """正確的閉包建立方式"""
    functions = []
    for i in range(3):
        functions.append(lambda x=i: x)  # 使用預設參數捕獲 i 的當前值
    return functions

print("\n錯誤方式:")
wrong_funcs = create_functions_wrong()
for func in wrong_funcs:
    print(func())  # 2, 2, 2

print("\n正確方式:")
correct_funcs = create_functions_correct()
for func in correct_funcs:
    print(func())  # 0, 1, 2

# 5. 裝飾器中的閉包
def timer_decorator(func):
    """計時裝飾器"""
    import time
    
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time:.4f} seconds")
        return result
    
    return wrapper

@timer_decorator
def slow_function():
    """模擬慢函數"""
    import time
    time.sleep(0.1)
    return "Done!"

result = slow_function()
print(f"Result: {result}")

# 6. 工廠函數和配置閉包
def create_validator(min_length=0, max_length=float('inf'), pattern=None):
    """建立驗證器閉包"""
    import re
    
    compiled_pattern = re.compile(pattern) if pattern else None
    
    def validate(value):
        if not isinstance(value, str):
            return False, "Value must be a string"
        
        if len(value) < min_length:
            return False, f"Value too short (min: {min_length})"
        
        if len(value) > max_length:
            return False, f"Value too long (max: {max_length})"
        
        if compiled_pattern and not compiled_pattern.match(value):
            return False, f"Value doesn't match pattern: {pattern}"
        
        return True, "Valid"
    
    return validate

# 建立不同的驗證器
email_validator = create_validator(5, 100, r'^[^@]+@[^@]+\.[^@]+$')
password_validator = create_validator(8, 50, r'^(?=.*[A-Za-z])(?=.*\d)')

# 測試驗證器
test_email = "user@example.com"
test_password = "mypass123"

email_valid, email_msg = email_validator(test_email)
password_valid, password_msg = password_validator(test_password)

print(f"Email '{test_email}': {email_valid} - {email_msg}")
print(f"Password '{test_password}': {password_valid} - {password_msg}")
```

```javascript !! js
// JavaScript 閉包機制

// 1. 基本閉包
function createMultiplier(factor) {
    // factor 被閉包捕獲
    return function(number) {
        return number * factor;
    };
}

// 使用箭頭函數的簡化版本
const createMultiplierArrow = factor => number => number * factor;

// 使用閉包
const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(`Double 5: ${double(5)}`);  // 10
console.log(`Triple 4: ${triple(4)}`);  // 12

// 2. 閉包修改外部變數
function createAccumulator(initial = 0) {
    let total = initial;
    
    function accumulate(value) {
        total += value;
        return total;
    }
    
    // 為函數新增額外方法
    accumulate.getTotal = () => total;
    accumulate.reset = () => {
        total = initial;
    };
    
    return accumulate;
}

const acc = createAccumulator(10);
console.log(`Accumulate 5: ${acc(5)}`);      // 15
console.log(`Accumulate 3: ${acc(3)}`);      // 18
console.log(`Current total: ${acc.getTotal()}`);  // 18
acc.reset();
console.log(`After reset: ${acc.getTotal()}`);    // 10

// 3. 使用物件回傳多個閉包函數
function createCounter(start = 0, step = 1) {
    let count = start;
    
    return {
        increment() {
            count += step;
            return count;
        },
        
        decrement() {
            count -= step;
            return count;
        },
        
        getValue() {
            return count;
        },
        
        reset() {
            count = start;
        }
    };
}

const counter = createCounter(0, 2);
console.log(`Initial: ${counter.getValue()}`);      // 0
console.log(`Increment: ${counter.increment()}`);   // 2
console.log(`Increment: ${counter.increment()}`);   // 4
console.log(`Decrement: ${counter.decrement()}`);   // 2

// 4. 閉包與迴圈變數（常見陷阱和解決方案）
console.log("\n=== Loop Variable Closure ===");

// 問題：使用 var（延遲綁定）
function createFunctionsWrongVar() {
    const functions = [];
    for (var i = 0; i < 3; i++) {
        functions.push(function() {
            return i;  // 所有函數共享同一個 i
        });
    }
    return functions;
}

// 解決方案1：使用 let（區塊作用域）
function createFunctionsWithLet() {
    const functions = [];
    for (let i = 0; i < 3; i++) {
        functions.push(function() {
            return i;  // 每個 i 都是獨立的
        });
    }
    return functions;
}

// 解決方案2：使用 IIFE 立即執行函數
function createFunctionsWithIIFE() {
    const functions = [];
    for (var i = 0; i < 3; i++) {
        functions.push((function(index) {
            return function() {
                return index;
            };
        })(i));
    }
    return functions;
}

// 解決方案3：使用 bind
function createFunctionsWithBind() {
    const functions = [];
    function returnValue(value) {
        return value;
    }
    
    for (var i = 0; i < 3; i++) {
        functions.push(returnValue.bind(null, i));
    }
    return functions;
}

// 測試不同方案
console.log("Using var (wrong):");
createFunctionsWrongVar().forEach(func => console.log(func()));  // 3, 3, 3

console.log("Using let:");
createFunctionsWithLet().forEach(func => console.log(func()));   // 0, 1, 2

console.log("Using IIFE:");
createFunctionsWithIIFE().forEach(func => console.log(func()));  // 0, 1, 2

console.log("Using bind:");
createFunctionsWithBind().forEach(func => console.log(func()));  // 0, 1, 2

// 5. 模組模式（Module Pattern）
const MyModule = (function() {
    // 私有變數和函數
    let privateVariable = 0;
    const privateArray = [];
    
    function privateFunction(value) {
        return value * 2;
    }
    
    // 公共 API
    return {
        // 公共方法
        addItem(item) {
            privateArray.push(item);
            privateVariable++;
        },
        
        getCount() {
            return privateVariable;
        },
        
        getItems() {
            return [...privateArray];  // 回傳副本
        },
        
        processItem(item) {
            return privateFunction(item);
        },
        
        clear() {
            privateArray.length = 0;
            privateVariable = 0;
        }
    };
})();

// 使用模組
MyModule.addItem("item1");
MyModule.addItem("item2");
console.log(`Module count: ${MyModule.getCount()}`);  // 2
console.log(`Module items: ${MyModule.getItems()}`);  // ["item1", "item2"]
console.log(`Process 5: ${MyModule.processItem(5)}`); // 10

// 6. 進階閉包：柯里化和偏函數應用
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...args2) {
                return curried.apply(this, args.concat(args2));
            };
        }
    };
}

// 原始函數
function add(a, b, c) {
    return a + b + c;
}

// 柯里化版本
const curriedAdd = curry(add);

// 不同的使用方式
console.log(curriedAdd(1)(2)(3));     // 6
console.log(curriedAdd(1, 2)(3));     // 6
console.log(curriedAdd(1)(2, 3));     // 6

// 建立專用函數
const add10 = curriedAdd(10);
const add10And5 = add10(5);
console.log(add10And5(3));  // 18

// 7. 記憶化（Memoization）閉包
function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            console.log(`Cache hit for ${key}`);
            return cache.get(key);
        }
        
        console.log(`Computing for ${key}`);
        const result = fn.apply(this, args);
        cache.set(key, result);
        
        return result;
    };
}

// 費波那契數列（遞迴版本）
const fibonacci = memoize(function(n) {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log(`Fibonacci(10): ${fibonacci(10)}`);
console.log(`Fibonacci(10) again: ${fibonacci(10)}`);  // 從快取取得
```
</PythonEditor>

## 高階函數和函數式程式設計

### 高階函數模式

<PythonEditor title="高階函數" compare={true}>
```python !! py
# Python 高階函數和函數式程式設計

# 1. 內建高階函數
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map: 應用函數到每個元素
squared = list(map(lambda x: x**2, numbers))
print(f"Squared: {squared}")

# filter: 過濾元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Evens: {evens}")

# reduce: 累積操作
from functools import reduce
sum_all = reduce(lambda acc, x: acc + x, numbers, 0)
product_all = reduce(lambda acc, x: acc * x, numbers, 1)
print(f"Sum: {sum_all}, Product: {product_all}")

# 2. 自訂高階函數
def apply_twice(func, value):
    """應用函數兩次"""
    return func(func(value))

def add_one(x):
    return x + 1

result = apply_twice(add_one, 5)  # ((5 + 1) + 1) = 7
print(f"Apply twice: {result}")

# 3. 函數組合
def compose(f, g):
    """函數組合：f(g(x))"""
    return lambda x: f(g(x))

def multiply_by_2(x):
    return x * 2

def subtract_1(x):
    return x - 1

# 組合函數：先乘以2，再減1
composed = compose(subtract_1, multiply_by_2)
result = composed(5)  # (5 * 2) - 1 = 9
print(f"Composed function: {result}")

# 4. 多函數組合
def pipe(*functions):
    """管道操作：從左到右應用函數"""
    return lambda x: reduce(lambda acc, func: func(acc), functions, x)

# 建立處理管道
process = pipe(
    lambda x: x * 2,      # 乘以2
    lambda x: x + 10,     # 加10
    lambda x: x ** 2      # 平方
)

result = process(3)  # ((3 * 2) + 10) ** 2 = 16 ** 2 = 256
print(f"Pipeline result: {result}")

# 5. 裝飾器作為高階函數
def log_calls(func):
    """記錄函數呼叫的裝飾器"""
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned: {result}")
        return result
    return wrapper

def retry(max_attempts=3):
    """重試裝飾器工廠"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"Attempt {attempt + 1} failed: {e}")
            
        return wrapper
    return decorator

@log_calls
@retry(max_attempts=2)
def risky_operation(x, y):
    """可能失敗的操作"""
    if x < 0:
        raise ValueError("x cannot be negative")
    return x / y

try:
    result = risky_operation(10, 2)
    print(f"Risky operation result: {result}")
except Exception as e:
    print(f"Operation failed: {e}")

# 6. 柯里化實作
def curry(func):
    """柯里化函數"""
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= func.__code__.co_argcount:
            return func(*args, **kwargs)
        return lambda *more_args, **more_kwargs: curried(*(args + more_args), **{**kwargs, **more_kwargs})
    return curried

@curry
def add_three(a, b, c):
    return a + b + c

# 使用柯里化函數
add_1_2 = add_three(1, 2)
result = add_1_2(3)  # 6
print(f"Curried function: {result}")

# 7. 偏函數應用
from functools import partial

def multiply(x, y, z):
    return x * y * z

# 建立偏函數
double = partial(multiply, 2)      # 固定第一個參數為2
result = double(3, 4)              # 2 * 3 * 4 = 24
print(f"Partial function: {result}")

# 8. 函數式資料處理
def process_data(data):
    """函數式資料處理流水線"""
    return pipe(
        lambda items: filter(lambda x: x > 0, items),           # 過濾正數
        lambda items: map(lambda x: x * 2, items),              # 乘以2
        lambda items: filter(lambda x: x % 4 == 0, items),      # 過濾能被4整除的
        list                                                    # 轉換為清單
    )(data)

test_data = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
processed = process_data(test_data)
print(f"Processed data: {processed}")

# 9. 遞迴高階函數
def fold_right(func, initial, iterable):
    """右摺疊操作"""
    items = list(iterable)
    if not items:
        return initial
    return func(items[0], fold_right(func, initial, items[1:]))

def fold_left(func, initial, iterable):
    """左摺疊操作"""
    result = initial
    for item in iterable:
        result = func(result, item)
    return result

# 測試摺疊操作
numbers = [1, 2, 3, 4, 5]
sum_right = fold_right(lambda x, acc: x + acc, 0, numbers)
sum_left = fold_left(lambda acc, x: acc + x, 0, numbers)
print(f"Right fold sum: {sum_right}")
print(f"Left fold sum: {sum_left}")

# 10. 產生器和高階函數
def generate_fibonacci():
    """費波那契數列產生器"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

def take(n, iterable):
    """取前n個元素"""
    for i, item in enumerate(iterable):
        if i >= n:
            break
        yield item

def map_gen(func, iterable):
    """惰性map操作"""
    for item in iterable:
        yield func(item)

# 使用產生器的函數式操作
fib_squares = map_gen(lambda x: x**2, take(10, generate_fibonacci()))
result = list(fib_squares)
print(f"First 10 Fibonacci squares: {result}")
```

```javascript !! js
// JavaScript 高階函數和函數式程式設計

// 1. 陣列的內建高階函數
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map: 應用函數到每個元素
const squared = numbers.map(x => x ** 2);
console.log(`Squared: ${squared}`);

// filter: 過濾元素
const evens = numbers.filter(x => x % 2 === 0);
console.log(`Evens: ${evens}`);

// reduce: 累積操作
const sumAll = numbers.reduce((acc, x) => acc + x, 0);
const productAll = numbers.reduce((acc, x) => acc * x, 1);
console.log(`Sum: ${sumAll}, Product: ${productAll}`);

// forEach: 副作用操作
console.log("Numbers:");
numbers.forEach((num, index) => {
    console.log(`${index}: ${num}`);
});

// 2. 自訂高階函數
function applyTwice(func, value) {
    return func(func(value));
}

const addOne = x => x + 1;
const result1 = applyTwice(addOne, 5);  // ((5 + 1) + 1) = 7
console.log(`Apply twice: ${result1}`);

// 3. 函數組合
const compose = (f, g) => x => f(g(x));

const multiplyBy2 = x => x * 2;
const subtract1 = x => x - 1;

// 組合函數：先乘以2，再減1
const composed = compose(subtract1, multiplyBy2);
const result2 = composed(5);  // (5 * 2) - 1 = 9
console.log(`Composed function: ${result2}`);

// 4. 多函數組合（管道操作）
const pipe = (...functions) => x => functions.reduce((acc, func) => func(acc), x);

// 建立處理管道
const process = pipe(
    x => x * 2,      // 乘以2
    x => x + 10,     // 加10
    x => x ** 2      // 平方
);

const result3 = process(3);  // ((3 * 2) + 10) ** 2 = 16 ** 2 = 256
console.log(`Pipeline result: ${result3}`);

// 5. 裝飾器模式的高階函數
function logCalls(func) {
    return function(...args) {
        console.log(`Calling ${func.name} with args: ${args}`);
        const result = func.apply(this, args);
        console.log(`${func.name} returned: ${result}`);
        return result;
    };
}

function retry(maxAttempts = 3) {
    return function(func) {
        return function(...args) {
            for (let attempt = 0; attempt < maxAttempts; attempt++) {
                try {
                    return func.apply(this, args);
                } catch (error) {
                    if (attempt === maxAttempts - 1) {
                        throw error;
                    }
                    console.log(`Attempt ${attempt + 1} failed: ${error.message}`);
                }
            }
        };
    };
}

// 應用裝飾器
const riskyOperation = retry(2)(logCalls(function riskyOp(x, y) {
    if (x < 0) {
        throw new Error("x cannot be negative");
    }
    return x / y;
}));

try {
    const result = riskyOperation(10, 2);
    console.log(`Risky operation result: ${result}`);
} catch (error) {
    console.log(`Operation failed: ${error.message}`);
}

// 6. 柯里化實作
function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...args2) {
                return curried.apply(this, args.concat(args2));
            };
        }
    };
}

const addThree = (a, b, c) => a + b + c;
const curriedAddThree = curry(addThree);

// 使用柯里化函數
const add12 = curriedAddThree(1, 2);
const result4 = add12(3);  // 6
console.log(`Curried function: ${result4}`);

// 自動柯里化的語法糖
const curriedMultiply = a => b => c => a * b * c;
const result5 = curriedMultiply(2)(3)(4);  // 24
console.log(`Auto-curried: ${result5}`);

// 7. 偏函數應用
function partial(fn, ...partialArgs) {
    return function(...remainingArgs) {
        return fn(...partialArgs, ...remainingArgs);
    };
}

const multiply = (x, y, z) => x * y * z;

// 建立偏函數
const double = partial(multiply, 2);      // 固定第一個參數為2
const result6 = double(3, 4);             // 2 * 3 * 4 = 24
console.log(`Partial function: ${result6}`);

// 8. 函數式資料處理
function processData(data) {
    return data
        .filter(x => x > 0)           // 過濾正數
        .map(x => x * 2)              // 乘以2
        .filter(x => x % 4 === 0);    // 過濾能被4整除的
}

const testData = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8];
const processed = processData(testData);
console.log(`Processed data: ${processed}`);

// 9. 函數式程式設計工具函數
const FP = {
    // 惰性求值
    lazy(fn) {
        let cached = false;
        let result;
        
        return function() {
            if (!cached) {
                result = fn.apply(this, arguments);
                cached = true;
            }
            return result;
        };
    },
    
    // 記憶化
    memoize(fn) {
        const cache = new Map();
        
        return function(...args) {
            const key = JSON.stringify(args);
            
            if (cache.has(key)) {
                return cache.get(key);
            }
            
            const result = fn.apply(this, args);
            cache.set(key, result);
            return result;
        };
    },
    
    // 防抖
    debounce(fn, delay) {
        let timeoutId;
        
        return function(...args) {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => fn.apply(this, args), delay);
        };
    },
    
    // 節流
    throttle(fn, limit) {
        let inThrottle;
        
        return function(...args) {
            if (!inThrottle) {
                fn.apply(this, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    }
};

// 測試工具函數
const expensiveCalculation = FP.memoize(function(n) {
    console.log(`Computing fibonacci(${n})`);
    if (n < 2) return n;
    return expensiveCalculation(n - 1) + expensiveCalculation(n - 2);
});

console.log(`Fibonacci(10): ${expensiveCalculation(10)}`);
console.log(`Fibonacci(10) again: ${expensiveCalculation(10)}`);  // 從快取取得

// 10. 函數式資料結構
class FunctionalArray {
    constructor(items = []) {
        this.items = [...items];
    }
    
    map(fn) {
        return new FunctionalArray(this.items.map(fn));
    }
    
    filter(fn) {
        return new FunctionalArray(this.items.filter(fn));
    }
    
    reduce(fn, initial) {
        return this.items.reduce(fn, initial);
    }
    
    flatMap(fn) {
        return new FunctionalArray(this.items.flatMap(fn));
    }
    
    take(n) {
        return new FunctionalArray(this.items.slice(0, n));
    }
    
    drop(n) {
        return new FunctionalArray(this.items.slice(n));
    }
    
    toArray() {
        return [...this.items];
    }
    
    // 支援鏈式呼叫
    pipe(...functions) {
        return functions.reduce((acc, func) => func(acc), this);
    }
}

// 使用函數式陣列
const functionalResult = new FunctionalArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    .filter(x => x % 2 === 0)     // [2, 4, 6, 8, 10]
    .map(x => x * x)              // [4, 16, 36, 64, 100]
    .take(3)                      // [4, 16, 36]
    .toArray();

console.log(`Functional array result: ${functionalResult}`);

// 11. 非同步高階函數
async function asyncMap(array, asyncFn) {
    const promises = array.map(asyncFn);
    return Promise.all(promises);
}

async function asyncFilter(array, asyncPredicate) {
    const results = await asyncMap(array, asyncPredicate);
    return array.filter((_, index) => results[index]);
}

// 模擬非同步操作
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function isEvenAsync(num) {
    await delay(10);  // 模擬非同步操作
    return num % 2 === 0;
}

async function doubleAsync(num) {
    await delay(10);  // 模擬非同步操作
    return num * 2;
}

// 使用非同步高階函數
(async () => {
    const nums = [1, 2, 3, 4, 5];
    
    const evens = await asyncFilter(nums, isEvenAsync);
    console.log(`Async filtered evens: ${evens}`);
    
    const doubled = await asyncMap(nums, doubleAsync);
    console.log(`Async mapped doubled: ${doubled}`);
})();
```
</PythonEditor>

## 總結

本模組深入比較了 Python 和 JavaScript 的函數機制：

### 主要差異：

1. **函數定義**: JavaScript 有多種定義方式（宣告、表達式、箭頭函數），Python 主要使用 `def` 和 `lambda`
2. **作用域規則**: JavaScript 有函數作用域和區塊級作用域，Python 有 LEGB 規則
3. **變數提升**: JavaScript 存在提升現象，Python 沒有
4. **參數處理**: JavaScript 使用解構和其餘參數，Python 使用 `*args` 和 `**kwargs`
5. **閉包行為**: 兩者都支援閉包，但在迴圈變數捕獲上有不同的陷阱

### 關鍵概念：

1. **this 綁定**: JavaScript 特有的動態 this 綁定機制
2. **箭頭函數**: JavaScript ES6+ 的簡化語法和詞法 this
3. **高階函數**: 兩種語言都支援函數式程式設計模式
4. **效能優化**: 記憶化、尾遞迴優化、批次處理等技術

理解這些函數機制差異對於有效地從 Python 過渡到 JavaScript 開發至關重要。下一個模組將探討非同步程式設計這個 JavaScript 的核心特性。
